package com.token.iot.bratterycharging.gateway.server;

import java.util.Date;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.stereotype.Component;

import com.google.gson.Gson;
import com.outstanding.framework.base.mq.MQProducer;
import com.outstanding.framework.core.Logger;
import com.outstanding.framework.core.PendingException;
import com.outstanding.framework.core.TransactionContext;
import com.outstanding.framework.log.LoggerFactory;
import com.token.iot.bratterycharging.gateway.ChannelHandlerContextManager;

import com.token.iot.bratterycharging.gateway.IMEI;
import com.token.iot.bratterycharging.gateway.InvalidOper;
import com.token.iot.bratterycharging.gateway.Oper;
import com.token.iot.bratterycharging.gateway.OperFactory;
import com.token.iot.bratterycharging.gateway.OperParser;
import com.token.iot.bratterycharging.gateway.Sender;
import com.token.iot.bratterycharging.gateway.SenderFactory;
import com.token.iot.bratterycharging.gateway.api.Constant;
import com.token.iot.bratterycharging.gateway.api.Device;
import com.token.iot.bratterycharging.gateway.api.DeviceException;
import com.token.iot.bratterycharging.gateway.dao.ExceptionDAO;
import com.token.iot.bratterycharging.gateway.dao.InvalidOperDAO;
import com.token.iot.bratterycharging.gateway.dao.OfflineDAO;
import com.token.iot.bratterycharging.gateway.utils.ByteUtil;
import com.token.iot.bratterycharging.gateway.utils.ChannelHandlerContextUtil;

import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;

@Component("bratterChargingHandler")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class BratteryChargingServerHandler extends ChannelHandlerAdapter {

	private static Logger loger = LoggerFactory.getLogger(BratteryChargingServerHandler.class);

	@Autowired
	private InvalidOperDAO dao;

	@Autowired
	private ExceptionDAO exceptionDao;

	@Autowired
	private OfflineDAO offlineDao;

	@Autowired
	private OperService operService;

	@Autowired
	private SenderService senderService;

	@Autowired
	private MQProducer mQProducer;

	/**
	 * 建立连接时，服务器会要求设备请求一个设备ID号
	 */
	@Override
	public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
		try {
			String getDevice = "_020ADV000000/IMEI";
			byte[] b = getDevice.getBytes();
			byte[] c = ByteUtil.append(b, ByteUtil.END);
			loger.debug("Get IMEI command has been sended" + new String(c));
			ctx.writeAndFlush(c);
			super.handlerAdded(ctx);
		} catch (Exception e) {
			try {
				Throwable t = e.getCause();
				String context = e.getMessage();
				if (t != null) {
					context = " case:" + t.getMessage();
				}
				exceptionDao.insert(context);
				ctx.channel().close();
				ctx.close();
			} catch (Exception ee) {
				loger.error("exceptionDao read has exception", ee);
			}
			throw e;
		}
		// loger.debug("get client command has been sended");
	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		try {
			byte[] temp = (byte[]) msg;
			if (temp == null || temp.length < 4 || (temp[0] != ByteUtil.START)) {
				loger.error("channelRead error" + new String(temp));
//				String imei = IMEI.getIMEIEnableNull(ctx);
//				if (StringUtils.isNotBlank(imei)) {
//					ChannelHandlerContextManager.remove(imei);
//				}
				ctx.channel().close();
				ctx.close();
				return;
			}
			byte[] command = ByteUtil.removeFlag(temp);
			String str = new String(command);
			loger.info("The Receive Oper =" + str);
			OperParser parser = new OperParser();
			String imei = IMEI.getIMEIEnableNull(ctx);
			
			//上行指令响应器
			Oper o = null;
			String oper = parser.parser(command);
			o = OperFactory.createOper(oper, imei);
			// 返回1，则表示该上行需要有对应的下行指令
			int i = operService.process(o, command, ctx);
			if (i == 1) {
				String senderName = (String) TransactionContext.getData(Constant.SENDER);
				if (senderName == null) {
					senderName = oper;
				}
				Sender sender = SenderFactory.createSender(senderName, imei);
				Map map = (Map) TransactionContext.getData(Constant.PARAMS);
				senderService.process(sender, imei, map);
				return;
			}
		} catch (Exception e) {
			try {
				Throwable t = e.getCause();
				String context = e.getMessage();
				if (t != null) {
					context = " case:" + t.getMessage();
				}
				exceptionDao.insert(context);
				ctx.channel().close();
				ctx.close();
			} catch (Exception ee) {
				loger.error("exceptionDao read has exception", ee);
			}
			throw e;
		} finally {
			// 关闭业务上下文
			TransactionContext.clear();
		}

	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		try {
			super.channelActive(ctx);
			loger.info("The channel ");
		} catch (Exception e) {
			try {
				Throwable t = e.getCause();
				String context = e.getMessage();
				if (t != null) {
					context = " case:" + t.getMessage();
				}
				exceptionDao.insert(context);
			} catch (Exception ee) {
				loger.error("exceptionDao read has exception", ee);
			}
			throw e;
		}
	}

	// 断开了连接
	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		super.channelInactive(ctx);
		channelClosed(ctx);
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		try {
			super.exceptionCaught(ctx, cause);
			String ip = ChannelHandlerContextUtil.getIP(ctx);
			String imei = IMEI.getIMEIEnableNull(ctx);
//			if (StringUtils.isNotBlank(imei)) {
//				ChannelHandlerContextManager.remove(imei);
//			}
			DeviceException de = new DeviceException();
			de.setImei(imei);
			de.setTime(new Date());
			de.setCause(cause.getMessage());
			Gson gs = new Gson();
			String str = gs.toJson(de);
			Device d = new Device();
			d.setImei(imei);
			d.setIp(ip);
			mQProducer.sendMessageByTopic(Constant.MESSAGEHEAD + Constant.QueueDeviceChannelException, str);
			loger.error("The channel excepiton!!!!!!!!!!!!!!!");
			ctx.channel().close();
			ctx.close();
		} catch (Exception e) {
			try {
				Throwable t = e.getCause();
				String context = e.getMessage();
				if (t != null) {
					context = " case:" + t.getMessage();
				}
				exceptionDao.insert(context);
			} catch (Exception ee) {
				loger.error("exceptionDao read has exception", ee);
			}
			throw e;
		}
	}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		try {
			ctx.channel().flush();
			super.channelReadComplete(ctx);
		} catch (Exception e) {
			try {
				Throwable t = e.getCause();
				String context = e.getMessage();
				if (t != null) {
					context = " case:" + t.getMessage();
				}
				exceptionDao.insert(context);
			} catch (Exception ee) {
				loger.error("exceptionDao read has exception", ee);
			}
			throw e;
		}
	}

	/**
	 * 一个i/o事件关联一个Channel，ChannelEvent在ChannelPipeline中被一系列的ChannelHandler处理。
	 * 
	 * 每个事件要么是upstream event(上游事件)，要么是downstream
	 * event(下游事件)，如果一个事件从管道中的第一个处理器游向最后一个处理器，我们叫它上游事件；如果一个事件从管道中的最后一个事件游向第一个事件，我们叫它下游事件，看ChannelPipeline的图就明白多了。具体点说，服务器接收一个信息，这个接收信息的事件叫上游事件；服务器发送一个信息，这个发送信息的事件叫下游事件。这个规则同样适用于客户端，在客户端接收信息的事件叫上游事件，发送信息的事件叫下游事件。以下是事件举例：
	 * 
	 * 上游事件：messageReceived、exceptionCaught、channelOpen、channelClosed、channelBound、channelUnbound、channelConnected、writeComplete、channelDisconnected、channelInterestChanged，还有两个额外的事件只用于父通道(可以有子通道的)：childChannelOpen、childChannelClosed；
	 * 
	 * 下游事件：write、bind、unbind、connect、disconnect、close；
	 */
	@Override
	public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
		super.disconnect(ctx, promise);
		channelClosed(ctx);
	}

	/**
	 * 业务上的连接断开逻辑
	 * 
	 * @param ctx
	 * @throws Exception
	 */
	private void channelClosed(ChannelHandlerContext ctx) throws Exception {
		try {

			String ip = ChannelHandlerContextUtil.getIP(ctx);
			String imei = IMEI.getIMEIEnableNull(ctx);
			if (StringUtils.isNotBlank(imei)) {
				ChannelHandlerContext context = ChannelHandlerContextManager.getChannelHandlerContextByImei(imei);
				if (ctx.equals(context)) {
					ChannelHandlerContextManager.remove(imei);
				}
			}
			Device d = new Device();
			d.setImei(imei);
			d.setIp(ip);

			try {
				offlineDao.insert(d);
			} catch (Exception e) {
				loger.error(e);
			}
			Gson gs = new Gson();
			String str = gs.toJson(d);
			// String queueName = "offLine";
			mQProducer.sendMessageByTopic(Constant.MESSAGEHEAD + Constant.QueueDeviceOFF, str);
			loger.error("The channel closed!!!!!!!!!!!!!!!");

		} catch (Exception e) {
			try {
				Throwable t = e.getCause();
				String context = e.getMessage();
				if (t != null) {
					context = " case:" + t.getMessage();
				}
				exceptionDao.insert(context);
			} catch (Exception ee) {
				loger.error("exceptionDao read has exception", ee);
			}
			throw e;
		}
	}

}
